KMP算法--Next数组详解与优化

本篇文章直接跳过蛮力算法以及一些简单背景,着重讨论Next数组的意义以及其是如何工作的,并对如何求Next数组做详细记录。

1.背景

1.1 KMP算法的应用:KMP算法用来解决模式串匹配问题。

1.2 为什么要用KMP算法:普通的蛮力算法时间复杂度为O(n*m),而KMP为O(n+m)。

2.KMP算法思想

2.1 KMP算法的思想:(称T为目标串,P为待查找字串)

  1. 目标串T的 i 指针不必回溯!
  2. 通过对待查找字串P进行分析得出当每次字符不匹配时P串应如何移动

2.2 Next数组介绍:Next数组中存的是如果P[ j ] != T[ i ] 时,j 应该等于多少。即P串要向右移动多少位,此时 i 不变。Next[ j]代表前 j - 1 个字符的最大前缀和最大后缀相同的字符数,这样当P[ j ] != T[ i ]时,将j = Next[ j ] ,由于前缀与后缀相等,故此时前 j 个字符仍是匹配的。

2.3 代码实例:

int KMP(string a,string b){
	BuildNext(b);//用来求Next数组
	int n = a.length();
	int m = b.length();
	int i = 0,j = 0;
	while(j < m && i < n){
		if(j < 0 || a[i] == b[j])
			i++,j++;
		else j = Next[j];
	}
	return i-j;
}

3.Next数组的求法

3.1 与KMP算法本身类似的思想:将P串自己与自己匹配。

3.2 代码实例:

void BuildNext(string P){
	int m = P.length();
	int t = Next[0] = -1;
	int j = 0;
	while(j < m-1){
		if(t < 0 || P[j] == P[t]){
			j++;
			t++;
			Next[j] = t;//待优化
		}else t = Next[t];
	}
}

3.3 解析:这里用到一个小技巧,令Next[0] = -1,叫做字符通配符,即可以和所有字符匹配,这样就可以在P没有前缀和T[i]匹配时P从头开始比较。

4.优化

4.1 对Next数组进行优化:当待匹配子串中有较多相同字符时,以上方法还是会进行很多次无谓的比较,比如T : 00100010与P :00010进行匹配,当知道T中的第三位1与P中的第三位0不匹配时,上述方法会将P串向右移动一位,结果仍是不匹配,但是我们已经知道了0和1不等,这样还有必要每次都向后移一位吗?为什么不直接移动3位?

4.2 实现方法:我们在求前 i - 1 个元素前缀与后缀相等个数的时候,是不考虑第P[ i ]的,但是如果要是最大相等前缀的后一个字符和P[ i ]相等的话,不就表明即使你移动了之后,当前字符依然没变,也就依然不能匹配,所以要再接着移吗?

4.3 代码实例:

void BuildNext(string P){
	int m = P.length();
	int t = Next[0] = -1;
	int j = 0;
	while(j < m-1){
		if(t < 0 || P[j] == P[t]){
			j++;
			t++;
			Next[j] = P[j] != P[t]?t:Next[t];
		}else t = Next[t];
	}
}

5.完整代码
 

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1e5;
int Next[maxn];
void BuildNext(string P){
	int m = P.length();
	int t = Next[0] = -1;
	int j = 0;
	while(j < m-1){
		if(t < 0 || P[j] == P[t]){
			j++;
			t++;
			Next[j] = P[j] != P[t]?t:Next[t];
		}else t = Next[t];
	}
}
int KMP(string a,string b){
	BuildNext(b);
	int n = a.length();
	int m = b.length();
	int i = 0,j = 0;
	while(j < m && i < n){
		if(j < 0 || a[i] == b[j])
			i++,j++;
		else j = Next[j];
	}
	return i-j;
}
int main()
{
	string a,b;
	getline(cin,a);
	getline(cin,b);
	int pos;
	if(b.length() > a.length())	pos = -1;
	else pos = KMP(a,b);
	if(pos >= 0)	printf("%d\n",pos);
	else puts("NO");
} 

 

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。其中,KMP算法的关键在于求解模式串P的next数组。下面详细介绍next数组的含义、求解方法以及应用。 1. 含义 next数组是一个长度为m(m为模式串P的长度)的数组,其中next[i]表示P[0:i]这个子串中,最长的既是其前缀又是其后缀的字符串的长度。特别地,next[0]=-1,next[1]=0。例如,当P="abab"时,其next数组为[-1,0,0,1]。 2. 求解 next数组的求解可以通过动态规划的方式实现。具体来说,在求解next[i]时,假设已知next[0:i-1]的值,我们需要找到一个最长的既是P[0:i-1]的前缀,也是P[1:i]的后缀的字符串。这个字符串可以通过比较P[0:j-1]和P[i-j:i-1]来得到,其中j=next[i-1]+1。 如果P[j]==P[i],那么next[i]=j;否则,我们需要找到一个更短的字符串。此时,我们可以利用next数组的性质,从next[j]开始向前查找,直到找到一个P[k]等于P[i]为止,然后令next[i]=k。如果一直找到k=-1还没有找到,那么next[i]=0。 3. 应用 有了next数组之后,我们就可以利用KMP算法在文本串S中查找模式串P的出现位置。具体来说,我们维护两个指针i和j,分别指向S和P的当前位置。如果P[j]==S[i],那么i和j都向后移动一位;否则,我们利用next数组来决定j的下一步移动位置。具体来说,如果next[j]=-1,或者next[j]<i,则令j=0,i不变;否则,令j=next[j]。这样,我们可以在O(n+m)的时间复杂度内完成匹配。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迷亭1213

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值